#ifndef PRVP_NSMultiShift11_HPP_
#define PRVP_NSMultiShift11_HPP_

// Framework includes
#include "../OptFrame/NSSeq.hpp"

// Own includes
#include "ProblemInstance.hpp"
#include "Memory.h"
#include "Solution.h"

using namespace std;

struct movShift
{
	int r1, r2;//routes
	int c1, c2;//clients
	int p1, p2;//positions
};

class MoveMultiShift11: public Move<RepPRVP, MemPRVP>
{
private:
	vector<movShift>& movimentos;

public:

	using Move<RepPRVP, MemPRVP>::apply; // prevents name hiding
	using Move<RepPRVP, MemPRVP>::canBeApplied; // prevents name hiding

	MoveMultiShift11(vector<movShift>& _movimentos) :
		movimentos(_movimentos)
	{
	}

	virtual ~MoveMultiShift11()
	{
	}

	bool canBeApplied(const RepPRVP& rep)
	{
		return true;
	}

	Move<RepPRVP, MemPRVP>& apply(RepPRVP& rep)
	{
		movShift rev;
		vector<movShift>* movimentosRev = new vector<movShift> (rep.size());


		for (int p = 0; p < movimentos.size(); p++)
		{
			//definindo movimento nulo
			rev.r1 = -1;
			rev.r2 = -1;
			rev.c1 = -1;
			rev.c2 = -1;
			rev.p1 = -1;
			rev.p2 = -1;

			movShift& atual = movimentos.at(p);

			if (atual.r1 == -1)//movimento nulo
			{
				movimentosRev->push_back(rev);
			}
			else
			{
				//pegando os clientes
				int cli1 = rep.at(p).at(atual.r1).at(atual.c1);
				int cli2 = rep.at(p).at(atual.r2).at(atual.c2);

				//removendo os clientes de cada rota
				rep.at(p).at(atual.r1).erase(rep.at(p).at(atual.r1).begin() + atual.c1);
				rep.at(p).at(atual.r2).erase(rep.at(p).at(atual.r2).begin() + atual.c2);

				//verificando se a remoção faz mudar a posição de inserção
				if (atual.c1 < atual.p2)// o c1 estava antes da posição onde será inserido o c2
					atual.p2--;
				if (atual.c2 < atual.p1)// o c2 estava antes da posição onde será inserido o c1
					atual.p1--;

				//fazendo a inserção
				rep.at(p).at(atual.r1).insert(rep.at(p).at(atual.r1).begin() + atual.p2, cli2);
				rep.at(p).at(atual.r2).insert(rep.at(p).at(atual.r2).begin() + atual.p1, cli1);

				//definindo o movimento reverso
				rev.r1 = atual.r1;
				rev.r2 = atual.r2;
				rev.c1 = atual.p2;
				rev.c2 = atual.p1;
				rev.p1 = atual.c2;
				rev.p2 = atual.c1;

				movimentosRev->push_back(rev);

			}

		}
		return *new MoveMultiShift11(*movimentosRev);// SEM MOVIMENTO REVERSO
	}

	virtual bool operator==(const Move<RepPRVP, MemPRVP>& _m) const
	{
		const MoveMultiShift11& m = (const MoveMultiShift11&) _m;
		bool equal = true;

		if(movimentos.size() != m.movimentos.size())
			return false;

		for(int p = 0; p < movimentos.size(); p++)
		{
			equal = equal && (movimentos.at(p).r1 == m.movimentos.at(p).r1);
			equal = equal && (movimentos.at(p).r2 == m.movimentos.at(p).r2);
			equal = equal && (movimentos.at(p).c1 == m.movimentos.at(p).c1);
			equal = equal && (movimentos.at(p).c2 == m.movimentos.at(p).c2);
			equal = equal && (movimentos.at(p).p1 == m.movimentos.at(p).p1);
			equal = equal && (movimentos.at(p).p2 == m.movimentos.at(p).p2);

			if(! equal)
				return false;
		}

		return equal;
	}

	void print() const
	{
		cout << "MoveMultiShift11( ";

		for(int p = 0; p < movimentos.size(); p++)
		{
			cout<< " [ "<< p << " , ";
			cout<< movimentos.at(p).r1 << " , ";
			cout<< movimentos.at(p).r2 << " , ";
			cout<< movimentos.at(p).c1 << " , ";
			cout<< movimentos.at(p).c2 << " , ";
			cout<< movimentos.at(p).p1 << " , ";
			cout<< movimentos.at(p).p2 << " ] , ";
		}

		cout << " )" << endl;
	}
};

class NSMultiShift11: public NS<RepPRVP, MemPRVP>
{
private:
	vector<movShift>* movimentos;

public:

	using NS<RepPRVP, MemPRVP>::move; // prevents name hiding

	NSMultiShift11()
	{
	}

	virtual ~NSMultiShift11()
	{
		//delete movimentos;
	}

	virtual Move<RepPRVP, MemPRVP>& move(const RepPRVP& rep)
	{
		movimentos = new vector<movShift>;

		movShift aux;
		for (int p = 0; p < rep.size(); p++)
		{
			//definindo movimento nulo
			aux.r1 = -1;
			aux.r2 = -1;
			aux.c1 = -1;
			aux.c2 = -1;
			aux.p1 = -1;
			aux.p2 = -1;

			int r1 = rand() % rep.at(p).size();

			if (rep.at(p).at(r1).size() == 0)
			{
				movimentos->push_back(aux);
				continue;
			}

			int r2;
			do
			{
				r2 = rand() % rep.at(p).size();
			} while (r1 == r2);

			if (rep.at(p).at(r2).size() == 0)
			{
				movimentos->push_back(aux);
				continue;
			}

			int c1 = rand() % rep.at(p).at(r1).size();
			int c2 = rand() % rep.at(p).at(r2).size();

			int p1 = rand() % (rep.at(p).at(r2).size() + 1);
			int p2 = rand() % (rep.at(p).at(r1).size() + 1);

			aux.r1 = r1;
			aux.r2 = r2;
			aux.c1 = c1;
			aux.c2 = c2;
			aux.p1 = p1;
			aux.p2 = p2;

			movimentos->push_back(aux);

		}

		return *new MoveMultiShift11(*movimentos); // return a random move
	}

	virtual void print()
	{
	}
};

#endif /*PRVP_NSMultiShift11_HPP_*/

